from collections import Counter
from operator import attrgetter
from django.db import transaction
from django.db.models import sql, signals
from django.db.models.deletion import Collector


class BaseModelCollector(Collector):
    """关联对象收集器, 封装了对相关联对象的操作"""

    def delete(self):
        # 排序实例集合
        for model, instances in self.data.items():
            self.data[model] = sorted(instances, key=attrgetter("pk"))

        # 如果可以　将模型按适合数据库的顺序排列
        # 把不支持事务或不能约束检查的推迟到事务结束
        self.sort()

        # 统计每个模型要删除出的实例数
        deleted_counter = Counter()

        with transaction.atomic(using=self.using, savepoint=False):
            # 发送删除信号
            for model, obj in self.instances_with_model():
                if not model._meta.auto_created:
                    signals.pre_delete.send(
                        sender=model, instance=obj, using=self.using
                    )

            # 删除关联了实例的对象
            for qs in self.fast_deletes:
                del_cond = qs.model.OwnMeta.del_cond
                del_opt = qs.model.OwnMeta.del_opt
                qs.filter(**del_cond).update(**del_opt)
                deleted_counter[qs.model._meta.label] += len(qs)

            # 考虑 on_delete=[SET_NULL, SET_DEFAULT, SET_VALUE]的情况
            # 这里把一律把实例的字段 deleted 设为 True
            for model, instances_for_fieldvalues in self.field_updates.items():
                del_opt = model.OwnMeta.del_opt
                for *_, instances in instances_for_fieldvalues.items():
                    query = sql.UpdateQuery(model)
                    query.update_batch([obj.pk for obj in instances], del_opt, self.using)

            # 逆序实例集合
            for instances in self.data.values():
                instances.reverse()

            # 删除实例操作
            for model, instances in self.data.items():
                query = sql.UpdateQuery(model)
                pk_list = [obj.pk for obj in instances]
                del_opt = model.OwnMeta.del_opt
                query.update_batch(pk_list, del_opt, self.using)
                count = len(pk_list)
                if count:
                    deleted_counter[model._meta.label] += count

                # 发送删除信号
                if not model._meta.auto_created:
                    for obj in instances:
                        signals.post_delete.send(
                            sender=model, instance=obj, using=self.using
                        )

        return sum(deleted_counter.values()), dict(deleted_counter)
